/* *****************************************************************************
 * JFab (http://code.google.com/p/jfab)
 * Copyright (c) 2011 JFab.org
 * Admin jfab@jeffreythompson.net
 *
 * See the file "LICENSE.txt" for information on usage and redistribution of
 * this file, and for a DISCLAIMER OF ALL WARRANTIES.
 * *****************************************************************************/

package org.jfab.core.io;

import java.io.Writer;

import org.apache.commons.lang3.Validate;
import org.jfab.core.collection.CGAnnotationList;
import org.jfab.core.collection.CGParameterList;
import org.jfab.core.collection.CGTypeList;
import org.jfab.core.collection.CommentList;
import org.jfab.core.collection.ModifierList;
import org.jfab.core.factory.StringFactory;
import org.jfab.core.model.CGMethodDeclaration;
import org.jfab.core.model.CGType;

/**
 * Provides a method declaration writer.
 */
public final class CGMethodDeclarationWriter implements
        CGWriter<CGMethodDeclaration>
{
    /** Basic code generator. */
    private final BasicCodeGenerator _basicCodeGenerator;

    /** String factory. */
    private final StringFactory _stringFactory;

    /**
     * Construct this object with the given parameters.
     * 
     * @param stringFactory String factory.
     * @param basicCodeGenerator Basic code generator.
     */
    public CGMethodDeclarationWriter(final StringFactory stringFactory,
            final BasicCodeGenerator basicCodeGenerator)
    {
        _stringFactory = stringFactory;
        _basicCodeGenerator = basicCodeGenerator;
    }

    /**
     * Write a method.
     * 
     * @param writer Writer.
     * @param method Method.
     */
    @Override
    public final void write(final Writer writer,
            final CGMethodDeclaration method)
    {
        Validate.notNull(method, "method is null");

        final CommentList commentLines = new CommentList(
                method.getCommentLines());
        final CGAnnotationList annotations = new CGAnnotationList(
                method.getAnnotations());
        final ModifierList modifiers = new ModifierList(method.getModifiers());
        final CGType returnType = method.getReturnType();
        final String methodName = method.getName();
        final CGParameterList parameters = new CGParameterList(
                method.getParameters());
        final CGTypeList throwsTypes = new CGTypeList(method.getThrowsTypes());

        getBasicCodeGenerator().writeMultilineJavadocComment(writer,
                commentLines);

        final boolean isSemiColonAdded = true;
        writeMethodDeclaration(writer, annotations, modifiers, returnType,
                methodName, parameters, throwsTypes, isSemiColonAdded);
    }

    /**
     * Write a method declaration.
     * 
     * @param writer Writer.
     * @param annotations Annotations.
     * @param modifiers Modifiers (@see java.lang.reflect.Modifier)
     * @param returnType Return type.
     * @param methodName Method name. (required)
     * @param parameters Parameters.
     * @param throwsTypes Throws types.
     * @param isSemicolonAdded Flag indicating if a semicolon is added.
     */
    protected void writeMethodDeclaration(final Writer writer,
            final CGAnnotationList annotations, final ModifierList modifiers,
            final CGType returnType, final String methodName,
            final CGParameterList parameters, final CGTypeList throwsTypes,
            final boolean isSemicolonAdded)
    {
        Validate.notEmpty(methodName, "methodName is empty");

        final StringFactory stringFactory = getStringFactory();

        final String annotationsString = stringFactory
                .createAnnotationsString(annotations);
        final String modifiersString = stringFactory
                .createModifiersString(modifiers);
        final String returnTypeString = stringFactory
                .createReturnTypeString(returnType);
        final String parametersString = stringFactory
                .createParametersString(parameters);
        final String throwsString = stringFactory
                .createThrowsString(throwsTypes);

        final StringBuilder sb = new StringBuilder();
        sb.append(annotationsString);
        sb.append(modifiersString);
        sb.append(returnTypeString);
        sb.append(" ");
        sb.append(methodName);
        sb.append("(");
        sb.append(parametersString);
        sb.append(")");
        sb.append(throwsString);

        if (isSemicolonAdded)
        {
            sb.append(";");
        }

        getBasicCodeGenerator().writeLine(writer, sb.toString());
    }

    /**
     * @return the basicCodeGenerator
     */
    private BasicCodeGenerator getBasicCodeGenerator()
    {
        return _basicCodeGenerator;
    }

    /**
     * @return the stringFactory
     */
    private StringFactory getStringFactory()
    {
        return _stringFactory;
    }
}
